// Copyright 2010 Google Inc. All Rights Reserved.

package com.google.zahra.testmanager;

import com.google.zahra.Master;

import org.terracotta.workmanager.queue.Queue;
import org.terracotta.workmanager.queue.ListBasedQueue;
import org.terracotta.workmanager.routing.RoutableWorkItem;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import javax.swing.table.DefaultTableModel;

/**
 * @author arthurkok@google.com (Your Name Here)
 *
 */
public class TestManager {

  private static final Queue.Factory<RoutableWorkItem<String>> queueFactory =
      new ListBasedQueue.Factory<RoutableWorkItem<String>>(1000);

  private final RoutableWorkItem.Factory<String> workItemFactory =
    new RoutableWorkItem.Factory<String>();
  
  // Each OS has an associated pending test queue. The key is the OS code and
  // the value is the queue
  private final Map<Integer, Queue<RoutableWorkItem<String>>> pendingTests =
      new ConcurrentHashMap<Integer, Queue<RoutableWorkItem<String>>>();
  
  // A table that stores every job and workitems created
  public final DefaultTableModel model =
      new DefaultTableModel(new Object[] {"Job ID", "Windows", "Mac", "Unix"}, 0);
  
  /**
   * Constructor. Initializes the {@code pendingTests} map if it hasn't been
   * already
   */
  public TestManager() {
        
    synchronized (pendingTests) {      
      for (int os : Master.listOfOs) {      
        if (!pendingTests.containsKey(os)) {
          pendingTests.put(os, queueFactory.create());
        }
      }
    }
  }  

  /**
   * Add a new test case.  
   * @param file The Python script
   * @param name Name of the PyUnit test to run. If none is specified, all test will be ran
   * @param os OS to run the test on
   * @param retries Number of retries
   */
  public void addTestCase(String file, String name, int os, int retries) {
    addTestCase(model.getRowCount(), file, name, os, retries);
  }

  /**
   * Add a new test case with a specific Job ID
   * @param jobId Job ID to create the test with
   * @param file The Python script
   * @param name Name of the PyUnit test to run. If none is specified, all test will be ran
   * @param os OS to run the test on
   * @param retries Number of retries
   */
  public void addTestCase(int jobId, String file, String name, int os, int retries) {

    Object[] rowData = new Object[4]; // A new entry in the table
    rowData[0] = jobId;

    System.out.println("Creating test with JobID:\t" + jobId);
    RoutableWorkItem<String> workItem = null;
    
    for (int code : Master.listOfOs) {
      // Bitmask the OS specified with the list of OS (win, mac, linux)
      if ((os & code) == code) {
        workItem = workItemFactory.create(new Test(file, name, code, retries, jobId), null, null);
        rowData[(int)(Math.log(code)/Math.log(2)) + 1] = workItem;
        addWorkItem(workItem);
      }
    }

    // Adding the row to the table
    synchronized (model) {
      // A Job ID less than the current row count means that job is being re-added
      // to the table
      if (jobId < model.getRowCount()) {
        model.setValueAt(rowData[(int)(Math.log(os)/Math.log(2)) + 1], jobId, (int)(Math.log(os)/Math.log(2)) + 1);
      } else {
        model.addRow(rowData);
      }
    }
  }

  /**
   * Adds a WorkItem to specified OS's pending queue
   * @param workItem WorkItem to add
   * @param os OS code
   */
  public void addWorkItem(RoutableWorkItem<String> workItem) {
    try {
      workItem.setStatus(-1, null); // Set status to WORK_CREATED
      pendingTests.get(((Test)workItem.getResult()).os).put(workItem);
    } catch (InterruptedException e) {
      Thread.interrupted();
      e.printStackTrace();
    }
  }

  /**
   * Clears all WorkItems in the table and also clears the pending tests queues
   */
  public void clearTable() {

    System.out.println("Clearing the Model");
    synchronized (model) {
      model.setRowCount(0);
    }

    System.out.println("Clearing pending tests");
    
    for (Queue<RoutableWorkItem<String>> queue : pendingTests.values()) {
      queue.clear();
    }
  }

  public long getCurrentJobId() {
    return model.getRowCount();
  }

  public RoutableWorkItem<String> getNextPendingTest(int os) {
    try {
      return pendingTests.get(os).take();
    } catch (InterruptedException e) {
      Thread.interrupted();
      e.printStackTrace();
      return null;
    }
  }
  
  public int getNumberOfPendingTests(int os) {
    return pendingTests.get(os).size();
  }

  public boolean hasPendingTests(int os) {
    return pendingTests.get(os).size() != 0;
  }

  public RoutableWorkItem<String> peekNextPendingTest(int os) {
    try {
      return pendingTests.get(os).peek();
    } catch (InterruptedException e) {
      Thread.interrupted();
      e.printStackTrace();
      return null;
    }
  }

}
